#ifndef PRYN_MONITOR_H
#define PRYN_MONITOR_H

#include <pryn/platform.h>


#include <pryn/common.h>
#include <pryn/change.h>
#include <pryn/object.h>
#include <pryn/result.h>
#include <pryn/tag.h>

PrynCallbackTypedef (PrynResult, PrynMonitorNotifyFunction, (PrynChange *change, void *data)) ///< Function called when an object being monitored has changed.

/** @defgroup PrynMonitor PrynMonitor C API
	@{ */

PrynImport (PrynResult, prynMonitorCreate, (PrynMonitor **monitor, const PrynObject *object, PrynChangeType changes, PrynMonitorNotifyFunction notify, void *data, PrynDeleteDataFunction destroyData))
	/**< @brief Create a new monitor for the object.

	This will receive changes to the object as well as changes to children objects. For example, a component receives changes about its pins, a factory receives changes about a component, a library receives changes about a factory, and a state receives changes about a library.

	@param[out] monitor Receives a pointer to the new monitor. This parameter is optional.
	@param object The object to attach a monitor to. The object must be of a type with monitors; #prynObjectMonitors would return success.
	@param changes What type of changes this monitor can be notified of. This can be a class or a specific change type. To be notified of all changes, pass #PrynChangeTypeAll.
	@param notify The function to notify of changes.
	@param data A data parameter provided to the function when notified.
	@param destroyData When the monitor is deleted, this is called with the data parameter to destroy it.
	@returns #PrynResultSuccess or an error code; see #PrynResult. */

PrynImport (PrynResult, prynMonitorDestroy, (PrynMonitor *monitor)) ///< Destroy a monitor, freeing all its data. Using the pointer beyond this function call will result in program corruption.

PrynImport (PrynResult, prynMonitorState, (PrynMonitor *monitor, PrynState **output)) ///< State the monitor is created within.
PrynImport (PrynResult, prynMonitorTags, (PrynMonitor *monitor, PrynTagList **output)) ///< List of tags associated with the monitor.

PrynImport (PrynResult, prynMonitorListDestroy, (PrynMonitorList *list)) ///< Destroy all members of the monitor list, then zero it out.

#ifdef PrynInternalStructs
/** @brief A monitor is sent information about actions performed on the objects it monitors.
	@ingroup PrynMonitor
*/
struct PrynMonitor
{
#ifdef PrynInternal
/** @name Internal fields
	These fields are not normally visible to clients, and it is recommended not to use them to maximise binary compatibility. To make them available, define PrynInternal.
	@{ */

	PrynCommonObject pCommonObject; ///< Common fields you'd expect in an object, like tags and monitors.
	
	PrynMonitor *pNext; ///< Next monitor in the list or 0 if this is last.
	PrynMonitor  *pPrevious; ///< Previous monitor in the list or 0 if this is first.
	
	PrynChangeType pChanges; ///< The type of changes this monitor can be notified of.
	PrynObject pObject; ///< The object that this monitors.
	PrynMonitorNotifyFunction pNotifyFunction; ///< Notification function.
	void *pData; ///< Data passed to the notification function.
	PrynDeleteDataFunction pDestroyData; ///< For destroying the data field when the monitor is destroyed, or 0.

/** @} */
#endif /* PrynInternal */

#if __cplusplus
	static inline PrynMonitor *Create (const PrynObject &object, PrynChangeType changes, PrynMonitorNotifyFunction notify, void *data = 0, PrynDeleteDataFunction destroyData = 0) { PrynMonitor *result; ((PrynObject) object).checkError (prynMonitorCreate (&result, &object, changes, notify, data, destroyData)); return result; }
		/**< @brief Create a new monitor for the object.

		This can receive changes to the object as well as changes to children objects. For example, a component receives changes about its pins, a factory receives changes about a component, a library receives changes about a factory, and a state receives changes about a library.

		@param object The object to attach a monitor to. The object must be of a type with monitors; object.monitors() would return non-zero.
		@param changes The type of changes to notify for. This can be a class or a specific change.
		@param notify The function to notify of changes.
		@param data A data parameter provided to the function when notified.
		@param destroyData A function that is passed the data parameter and is used to destroy it, or 0 if none is necessary.
		@returns The new monitor. */

	static inline PrynMonitor *Create (const PrynObject &object, PrynMonitorNotifyFunction notify, void *data = 0, PrynDeleteDataFunction destroyData = 0) { return Create (object, PrynChangeTypeAll, notify, data); }
		/**< @brief Create a new monitor for the object that is notified of all changes.

		This will receive changes to the object as well as changes to children objects. For example, a component receives changes about its pins, a factory receives changes about a component, a library receives changes about a factory, and a state receives changes about a library.

		@param object The object to attach a monitor to. The object must be of a type with monitors; object.monitors() would return non-zero.
		@param notify The function to notify of changes.
		@param data A data parameter provided to the function when notified.
		@param destroyData A function that is passed the data parameter and is used to destroy it, or 0 if none is necessary.
		@returns The new monitor. */

	inline PrynResult checkError (PrynResult result) { return prynCheckErrorBase (state (), result); } ///< If the result is below zero, throw an error.

	inline PrynState *state () { PrynState *result; prynCheckErrorNull (prynMonitorState (this, &result)); return result; } ///< The state the monitor is in.
	inline PrynTagList *tags () { PrynTagList *result; checkError (prynMonitorTags (this, &result)); return result; } ///< The list of tags attached to the monitor.
#endif /* __cplusplus */
};
#endif /* PrynInternalStructs */

/** @} */ // (group)

#endif /* PRYN_MONITOR_H */
